/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.zumbits.avd.comunication.protocol;

import br.com.zumbits.avd.comunication.Protocol;
import br.com.zumbits.avd.Constants;
import br.com.zumbits.avd.logger.Log;
import br.com.zumbits.avd.mysql.Mysql;
import br.com.zumbits.avd.mysql.UserSQL;
import br.com.zumbits.avd.storage.StorageManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

/**
 * Essa classe é responsável por realizar o tratamento de cada requisição dos
 * clientes.
 *
 * @author zumbits
 */
public class ReceiveProcess {

    private SAXBuilder saxBuilder;
    private Document document;
    private Protocol protocol;
    private StorageManager storegeManager;
    private HashMap<Integer, String> listLoggedUsers;
    private Mysql mysql;
    private UserSQL userSQL;

    public ReceiveProcess() {
        saxBuilder = new SAXBuilder();
        protocol = new Protocol();
        storegeManager = new StorageManager();
        listLoggedUsers = new HashMap<Integer, String>();

        mysql = new Mysql();
    }

    /**
     * Recebe a requisição
     *
     * @param request
     * @param response
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public String receive(HttpServletRequest request, HttpServletResponse response) throws JDOMException, IOException {
        //Converte o stream(arquivo xml) do cliente para uma string
        String xml = XML2String(request.getInputStream());

        Log.print("debug", "xml = \n" + xml);

        //inicia o documento xml
        document = saxBuilder.build(new StringReader(xml));
        //captura o tipo da requisição
        String todo = document.getRootElement().getName();

        Log.print("debug", "todo = " + todo);

        //Verifica qual a requisição
        if (todo.equals(Protocol.LOGIN)) {
            //Login
            Login login = protocol.login(xml);

            return authenticate(login.getUser(), login.getPassword());
        } else if (todo.equals(Protocol.LOGOFF)) {
            //Logoff
            Logoff logoff = protocol.logoff(xml);

            return disconnecting(Integer.parseInt(logoff.getSession().trim()), logoff.getName());
        } else if (todo.equals(Protocol.DOWNLOAD)) {
            //Download
            Download download = protocol.download(xml);

            int part = (download.getPart() != null) ? Integer.parseInt(download.getPart()) : 1;
            int totalParts = (download.getPart() != null)
                    ? Integer.parseInt(download.getTotalParts()) : 1;

            return download(Integer.parseInt(download.getSession().trim()),
                    download.getPath(),
                    download.getName(),
                    part,
                    totalParts);
        } else if (todo.equals(Protocol.UPLOAD)) {
            //Upload
            Upload upload = protocol.upload(xml);

            int part = (upload.getPart() != null) ? Integer.parseInt(upload.getPart()) : 1;
            int totalParts = (upload.getPart() != null)
                    ? Integer.parseInt(upload.getTotalParts()) : 1;

            return upload(Integer.parseInt(upload.getSession()),
                    upload.getPath(),
                    upload.getName(),
                    part,
                    totalParts,
                    upload.getData(),
                    Integer.parseInt(upload.getVersion()));
        } else if (todo.equals(Protocol.LIST)) {
            //List
            List list = protocol.list(xml);

            return list(Integer.parseInt(list.getSession().trim()), list.getPath());
        } else if (todo.equals(Protocol.MOVE)) {
            //Move
            Move move = protocol.move(xml);

            return move(Integer.parseInt(move.getSession().trim()), move.getOldPath(), move.getOldName(), move.getNewPath(), move.getNewName());
        } else if (todo.equals(Protocol.DELETE)) {
            //Delete
            Delete delete = protocol.delete(xml);

            return delete(Integer.parseInt(delete.getSession().trim()), delete.getPath(), delete.getName());
        } else if (todo.equals(Protocol.CREATE_FOLDER)) {
            //Create Folder
            CreateFolder createFolder = protocol.createFolder(xml);

            return createFolder(Integer.parseInt(createFolder.getSession().trim()), createFolder.getPath(), createFolder.getName());
        } else if (todo.equals(Protocol.COPY)) {
            //Copy
            Copy copy = protocol.copy(xml);

            return copy(Integer.parseInt(copy.getSession().trim()), copy.getPath(), copy.getName(), copy.getCopyPath());
        } else if (todo.equals(Protocol.VERSION)) {
            //Version
            Version version = protocol.version(xml);

            return version(Integer.parseInt(version.getSession().trim()), version.getPath(), version.getName());
        }

        return null;
    }

    /**
     * Converte um Stream de um xml para uma string
     * 
     * @param inputStream
     * @return
     * @throws IOException
     */
    public static String XML2String(InputStream inputStream) throws IOException {
        String xml = "";

        //Prepara um buffer para o stream
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

        String line = null;

        //Loop para ler o buffer. Lendo de linha em linha
        while ((line = bufferedReader.readLine()) != null) {
            //Concatenhando cada linha do buffer na string contendo o conteúdo do xml
            xml += line + "\n";
        }

        //Fechando os fluxos
        line = null;
        bufferedReader.close();
        inputStream.close();

        return xml;
    }

    /**
     * Verifica se o usuário está ou não na lista de usuários logados pelo o nome...
     * 
     * @param user
     * @return
     */
    public boolean isLogged(String user) {
        return listLoggedUsers.containsValue(user);
    }

    /**
     * Verifica se o usuário está ou não na lista de usuários logados pelo o id...
     *
     * @param id
     * @return
     */
    public boolean isLogged(int id) {
        return listLoggedUsers.containsKey(id);
    }

    /**
     * Valida a solicitação de login de um usuário. Ao logar-se ele recebe um id
     * único e aleatório. Caso ele já esteja logado, então é gerado um novo número
     * e destruída as sessões anteriores.
     * 
     * @param id       IP + porta de onde o usuário está acessando
     * @param user     Email do usuário
     * @param password Senha do usuário
     * @return Retorna um xml com o status de login
     */
    public String authenticate(String user, String password) {
        int status = Protocol.LOGIN_UNKNOWN;
        int id = -1;

        Random random = new Random();

        if (isLogged(user)) {
            //status = 2;
            //TODO: remove tudo que estava antes
            status = Protocol.LOGIN_SUCESS;
        } else {
            mysql.connect();
            userSQL = new UserSQL(mysql);
            try {
                ArrayList users = userSQL.select(user, null);

                if (((ArrayList) users.get(UserSQL.LOGIN)).size() == 0) {
                    status = Protocol.LOGIN_WRONG_AUTH;
                } else {
                    String passwordSQL = (String) ((ArrayList) users.get(UserSQL.SENHA)).get(0);

                    status = (password.equals(passwordSQL)) ? Protocol.LOGIN_SUCESS : Protocol.LOGIN_WRONG_AUTH;

                    passwordSQL = null;
                }
            } catch (SQLException ex) {
                Log.print("error", "Exception authenticate:\n" + ex.getMessage());
            }

            mysql.disconnect();
            userSQL = null;
        }

        Log.print("debug", "status = " + status);

        //SUCESS
        if (status == Protocol.LOGIN_SUCESS) {
            id = Math.abs(random.nextInt());

            while (listLoggedUsers.containsKey(id)) {
                id = random.nextInt();
            }

            listLoggedUsers.put(id, user);
            Log.print(user, "Logged");
        }

        String xml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerLogin>" + Constants.NEWLINE
                + Constants.TAB + "<session>" + id + "</session>" + Constants.NEWLINE
                + Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerLogin>";

        return xml;
    }

    /**
     * Realiza o logoff
     * 
     * @param user Email do usuário
     * @param id   IP + porta de onde o usuário está acessando
     * @return Retorna um xml com o status de logoff
     */
    public String disconnecting(int id, String user) {
        //erro genérico
        int status = Protocol.LOGOFF_UNKNOWN;

        if (isLogged(id)) {
            String user_ = listLoggedUsers.get(id);

            if (user_ != null && user_.equals(user)) {
                status = Protocol.LOGOFF_SUCESS;

                listLoggedUsers.remove(id);

                Log.print(user, "Disconected");
            }
        } else {
            status = Protocol.LOGOFF_NOT_LOGGED_IN;
        }

        String xml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerLogoff>" + Constants.NEWLINE
                + Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerLogoff>";

        return xml;
    }

    /**
     *
     * @param id
     * @param path
     * @param name
     * @return
     */
    private String download(int id, String path, String name, int part, int totalParts) {
        int status = Protocol.DOWNLOAD_UNKNOWN;
        Log.print("debug", "entrou no download");

        String xml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerDownload>" + Constants.NEWLINE
                + Constants.TAB + "<path>" + path + "</path>" + Constants.NEWLINE
                + Constants.TAB + "<name>" + name + "</name>" + Constants.NEWLINE;

        if (isLogged(id)) {
            String user = listLoggedUsers.get(id);

            if (storegeManager.exists(user, path, name)) {
                String fileURL_SAFE = storegeManager.fileURL_SAFE_B64(user, path, name, part, totalParts);

                if (fileURL_SAFE != null) {
                    if (part > 1 || totalParts > 1) {
                        xml +=
                                Constants.TAB + "<part>" + part + "</part>" + Constants.NEWLINE
                                + Constants.TAB + "<totalParts>" + totalParts + "</totalParts>" + Constants.NEWLINE;
                    }

                    xml += Constants.TAB + "<data>" + fileURL_SAFE + "</data>" + Constants.NEWLINE;
                    xml += Constants.TAB + "<version>" + storegeManager.getVersion(user, path, name) + "</version>" + Constants.NEWLINE;
                    status = Protocol.DOWNLOAD_SUCESS;
                }
            } else {
                status = Protocol.DOWNLOAD_FILE_NOT_FOUND;
            }
        } else {
            status = Protocol.DOWNLOAD_AUTH_NEEDED;
        }

        xml +=
                Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerDownload>";

        return xml;
    }

    /**
     * 
     * @param id
     * @param path
     * @param name
     * @param data
     * @param version
     * @return
     */
    private String upload(int id, String path, String name, int part, int totalParts, String data, int version) {
        String xml = "";
        int status = Protocol.UPLOAD_UNKNOWN;
        int versionServer = -1;

        xml +=
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerUpload>" + Constants.NEWLINE
                + Constants.TAB + "<path>" + path + "</path>" + Constants.NEWLINE
                + Constants.TAB + "<name>" + name + "</name>" + Constants.NEWLINE;

        if (isLogged(id)) {
            String user = listLoggedUsers.get(id);

            versionServer = storegeManager.getVersion(user, path, name);

            if (version > versionServer) {
                if (part > 1 || totalParts > 1) {
                    xml +=
                            Constants.TAB + "<part>" + part + "</part>" + Constants.NEWLINE
                            + Constants.TAB + "<totalParts>" + totalParts + "</totalParts>" + Constants.NEWLINE;
                }
                status = storegeManager.upload(user, path, name, part, totalParts, data, version);
                versionServer = version;
            } else {
                status = Protocol.UPLOAD_WRONG_VERSION;
            }
        } else {
            status = Protocol.UPLOAD_AUTH_NEEDED;
        }

        xml +=
                Constants.TAB + "<version>" + versionServer + "</version>" + Constants.NEWLINE
                + Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerUpload>";

        return xml;
    }

    /**
     * 
     * @param id
     * @param path
     * @return
     * @throws IOException
     */
    private String list(int id, String path) throws IOException {
        int status = Protocol.LIST_UNKNOWN;
        String xml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerList>" + Constants.NEWLINE
                + Constants.TAB + "<path>" + path + "</path>" + Constants.NEWLINE;

        if (!isLogged(id)) {
            status = Protocol.LIST_AUTH_NEEDED;
        } else {
            String user = listLoggedUsers.get(id);

            if (storegeManager.exists(user, path, "")) {
                String list = storegeManager.listFiles(user, path);

                if (list != null) {
                    status = Protocol.LIST_SUCESS;
                    xml += list;
                }
            } else {
                status = Protocol.LIST_INVALID_ARGS;
            }
        }

        xml +=
                Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerList>";

        return xml;
    }

    /**
     *
     * @param id
     * @param oldPath
     * @param oldName
     * @param newPath
     * @param newName
     * @return
     */
    private String move(int id, String oldPath, String oldName, String newPath, String newName) {
        String xml = "";
        int status = Protocol.MOVE_UNKNOWN;

        xml +=
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerMove>" + Constants.NEWLINE
                + Constants.TAB + "<oldPath>" + oldPath + "</odlPath>" + Constants.NEWLINE
                + Constants.TAB + "<oldName>" + oldName + "</oldName>" + Constants.NEWLINE
                + Constants.TAB + "<newPath>" + newPath + "</newPath>" + Constants.NEWLINE
                + Constants.TAB + "<newName>" + newName + "</newName>" + Constants.NEWLINE;

        if (!isLogged(id)) {
            status = Protocol.MOVE_AUTH_NEEDED;
        } else {
            String user = listLoggedUsers.get(id);

            if (storegeManager.move(user, oldPath, oldName, newPath, newName)) {
                status = Protocol.MOVE_SUCESS;
            } else {
                status = Protocol.MOVE_INVALID_ARGS;
            }
        }

        xml +=
                Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerMove>";

        return xml;
    }

    /**
     * 
     * @param id
     * @param path
     * @param name
     * @return
     */
    private String delete(int id, String path, String name) {
        String xml = "";
        int status = Protocol.DELETE_UNKNOWN;

        xml +=
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerDelete>" + Constants.NEWLINE
                + Constants.TAB + "<path>" + path + "</path>" + Constants.NEWLINE
                + Constants.TAB + "<name>" + name + "</name>" + Constants.NEWLINE;

        if (!isLogged(id)) {
            status = Protocol.DELETE_AUTH_NEEDED;
        } else {
            String user = listLoggedUsers.get(id);

            if (storegeManager.delete(user, path, name)) {
                status = Protocol.DELETE_SUCESS;
            } else {
                status = Protocol.DELETE_INVALID_ARGS;
            }
        }

        xml +=
                Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerDelete>";

        return xml;
    }

    /**
     * 
     * @param id
     * @param path
     * @param name
     * @return
     */
    private String createFolder(int id, String path, String name) {
        String xml = "";
        int status = Protocol.CREATEFOLDER_UNKNOWN;

        xml +=
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerCreateFolder>" + Constants.NEWLINE
                + Constants.TAB + "<path>" + path + "</path>" + Constants.NEWLINE
                + Constants.TAB + "<name>" + name + "</name>" + Constants.NEWLINE;

        if (!isLogged(id)) {
            status = Protocol.CREATEFOLDER_AUTH_NEEDED;
        } else {
            String user = listLoggedUsers.get(id);

            if (!storegeManager.exists(user, path, name)) {
                if (storegeManager.createFolder(user, path, name)) {
                    status = Protocol.CREATEFOLDER_SUCESS;
                } else {
                    status = Protocol.CREATEFOLDER_WRONG_ARGS;
                }
            } else {
                status = Protocol.CREATEFOLDER_ALREADY_EXIST;
            }
        }

        xml +=
                Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerCreateFolder>";

        return xml;
    }

    /**
     *
     * @param id
     * @param path
     * @param name
     * @param copyPath
     * @return
     * @throws IOException
     */
    private String copy(int id, String path, String name, String copyPath) throws IOException {
        String xml = "";
        int status = -1;

        xml +=
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerCopy>" + Constants.NEWLINE
                + Constants.TAB + "<path>" + path + "</path>" + Constants.NEWLINE
                + Constants.TAB + "<name>" + name + "</name>" + Constants.NEWLINE;

        if (!isLogged(id)) {
            status = 1;
        } else {
            String user = listLoggedUsers.get(id);

            if (storegeManager.copy(user, path, name, copyPath, name)) {
                status = 0;
            } else {
                status = 2;
            }
        }

        xml +=
                Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerCopy>";

        return xml;
    }

    /**
     * 
     * @param id
     * @param path
     * @param name
     * @return
     */
    private String version(int id, String path, String name) {
        int version = -1;
        int status = Protocol.VERSION_UNKNOWN;

        String xml =
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Constants.NEWLINE
                + "<answerVersion>" + Constants.NEWLINE
                + Constants.TAB + "<path>" + path + "</path>" + Constants.NEWLINE
                + Constants.TAB + "<name>" + name + "</name>" + Constants.NEWLINE;

        if (isLogged(id)) {
            String user = listLoggedUsers.get(id);

            if (storegeManager.exists(user, path, name)) {
                version = storegeManager.getVersion(user, path, name);
                status = (version > 0) ? Protocol.VERSION_SUCESS : Protocol.VERSION_UNKNOWN;
            } else {
                status = Protocol.VERSION_INVALID_ARGS;
            }
        } else {
            status = Protocol.VERSION_AUTH_NEEDED;
        }

        xml +=
                Constants.TAB + "<version>" + version + "</version>" + Constants.NEWLINE
                + Constants.TAB + "<status>" + status + "</status>" + Constants.NEWLINE
                + "</answerVersion>";

        return xml;
    }
}
